Load Libraries
Load all relevant R libraries.
Connect to Relational Database
# load the PostgreSQL driver
drv <- dbDriver("Postgres")
# create a connection to the postgres database
# set the search path to the mimiciii schema
con <- dbConnect(drv, dbname = "mimic",
host = "localhost", port = 5432,
user = "mousaghannam")
dbSendQuery(con, 'set search_path to mimiciii')
Show a list of all the tables in the relational database.
# show a list of tables
dbListTables(con)
Function for more easily pulling a given table.
This is a memory conservative approaching for performing SQL queries on DB. We can pull tables in memory and do certain analyses on them, without actually having to save a given object into the R environment.
#Memory conserative approaching for performing SQL queries on DB
#(Rewrite this function to handle multiple inputs )
tbl_mimic <- function(table) {
table <- as.character(substitute(table))
tbl(con, dbplyr::in_schema("mimiciii", table))
}
Create dictionary of diagnosis codes
This “dictionary” will contain all of the diagnoses events in the database associated with a given hospital admission id (hadm_id), along with a description of the icd9 code.
#Pull dictionary for diagnoses
d_icd_diagnoses <- tbl_mimic(d_icd_diagnoses) %>%
select(icd9_code, short_title)
diagnoses_icd <-tbl_mimic(diagnoses_icd) %>%
select(hadm_id, icd9_code, seq_num)
#ICD-9 Codes + their descriptions
icd9_dict <- diagnoses_icd %>%
inner_join(d_icd_diagnoses, by = "icd9_code")
icd9_dict
Load Electrolyte Lab Measurements
Determine the ids corresponding to lab values for each electrolyte
Saved all of the lab_ids into R environment with collect(), for filtering in the next step.
d_labitems <- tbl_mimic(d_labitems) #Pull dictionary for lab items into memory
d_labitems %>%
filter(str_detect(tolower(label), "calcium")) %>%
collect() -> lab_idsCa
lab_idsCa
Filter them
We are choosing to include the 1st and 4th row, which are strictly blood lab value draws for potassium.
#Drugs to include for potassium
itemidsCa <- lab_idsCa$itemid[c(4)]
Pull table containing all lab measurements.
We are now taking the table labevents, which contains all the lab events in the database, and selecting the rows associated with the two blood potassium lab values itemids that we chose above.
Join tables containing repletion events and lab events
Both the tables containing the lab draws and the repletion events have been extracted. We can now join them to begin analyzing the relationship between them.
In addition to joining the tables, we have renamed the column corresponding to the moment the fluid collection for the lab value was recorded, charttime, as charttime.lab for clarity.
repEventsCa %>%
inner_join(labEventsCa, by=c("subject_id" = "subject_id", "hadm_id" = "hadm_id")) %>%
distinct() %>%
rename(charttime.lab = charttime) %>%
collect() -> ca_lab_repletions_MV_new
ca_lab_repletions_MV_new
Find recent repletions
We want to flag all repletion events that occurred either 24 hours before or 24 hours after a given lab value. We then filter the table, storing all of the repletions occurring BEFORE a given lab value in one table, and all of the ones occurring AFTER a given lab value in another.
#Find the RECENT Repletions
ca_lab_repletions_MV_new %>%
mutate(charttime.lab= as_datetime(charttime.lab), endtime=as_datetime(endtime), starttime = as_datetime(starttime)) %>%
mutate(isRecentPre = difftime(starttime, charttime.lab, units = "hours") <= 24 & difftime(starttime, charttime.lab, units = "hours") > 0 ) %>%
mutate(isRecentPost = difftime(endtime, charttime.lab, units = "hours") >= -24 & difftime(endtime, charttime.lab, units = "hours") < 0 ) -> allRepLabEvents_ca
allRepLabEvents_ca %>%
filter(isRecentPre | isRecentPost)
Extract the most recent repletion, prior and after a given lab value.
We have a table that contains all of the repletion events that occur either 24 hours prior or after a given lab value. We then group the rows by each subject and hospital id, as well as the time of fluid acquisition for the lab value. Then, we create a column that generates a flag if the lab value that is chosen is the minimum starttime for all the possible starttimes. Because there are multiple lab values, as well as multiple repletion events, for a given hadm_id the table outputted has all the possible permutations. We want to select the most recent repletion AFTER a lab value, so we choose the repletion with the starttime equal to the minimum starttime for a given lab value.
#Potassium lab event PRE-repletions
allRepLabEvents_ca %>%
filter(isRecentPre) %>%
group_by(subject_id, hadm_id,charttime.lab) %>%
mutate(isMostRecentRepletion = starttime == min(starttime)) %>%
filter(isMostRecentRepletion) %>%
ungroup() %>%
group_by(subject_id, hadm_id, starttime) %>%
mutate(isMostRecentLabEvent = charttime.lab == max(charttime.lab)) %>%
filter(isMostRecentLabEvent) %>% distinct() -> pre_ca_lab_repletions_MV_new
pre_ca_lab_repletions_MV_new
For the post-repletion lab values, we are now doing the same thing, but instead finding the maximum endtime prior to a given lab value, to select the most recent repletion prior to a given lab value.
#Potassium lab event POST-repletions
allRepLabEvents_ca %>%
filter(isRecentPost) %>%
group_by(subject_id, hadm_id,charttime.lab) %>%
mutate(isMostRecentRepletion = endtime == max(endtime)) %>%
filter(isMostRecentRepletion) %>%
ungroup() %>%
group_by(subject_id, hadm_id, endtime) %>%
mutate(isMostRecentLabEvent = charttime.lab == min(charttime.lab)) %>%
filter(isMostRecentLabEvent) %>% distinct() -> post_ca_lab_repletions_MV_new
post_ca_lab_repletions_MV_new
We then combine all of the values from the “pre-repletion” and “post-repletion” tables, into one table. We create a new column preVsPost to indicate if a given event is either before or after a lab value. Conceivably, the same repletion could correspond to a
#Combine them into one dataset
ca_pre_post_repletions_MV_new <- bind_rows(list(pre_repletion = pre_ca_lab_repletions_MV_new, post_repletion = post_ca_lab_repletions_MV_new), .id = "preVsPost")
ca_pre_post_repletions_MV_new %>% distinct()
NA
Non Repletion Events
We will now determine the set of lab values for which there was no immediate repletion afterward.
Exclusions
We now want to exclude all the rows based on certain criteria.
We have a table called hadm_id_table which contains all of the hadm_ids of patients with diagnoses that would confound our results (kidney disease, etc). (WILL ADD HOW I CREATED THIS TABLE LATER).
hadm_id_table
As a result, we could simply do a semi-join to determine how many people in each group were excluded. Note, we are doing the anti-join on the table with the most recent repletion before and after a given lab result, within a 24 hour time range. We are not doing it on the set of ALL possible repletion and or lab result events.
hadm_id_table %>%
semi_join(ca_pre_post_repletions_MV_new, by = "hadm_id") %>%
select(hadm_id, .id) %>%
group_by(.id) %>%
summarize(n = n()) %>%
arrange(desc(n)) %>%
kable()
# ca_pre_post_repletions_MV_new %>%
# ungroup()%>%
# semi_join(hadm_id_table, by = "hadm_id") %>%
# select(icustay_id, orderid, .id) %>%
# group_by(.id) %>%
# summarize(n = n()) %>%
# arrange(desc(n)) %>%
# kable()
And here is the resultant dataset after actually excluding this time, using an anti-join instead.
ca_pre_post_repletions_MV_new %>%
anti_join(hadm_id_table, by = "hadm_id") -> postExclusionsCa_Repleted
postExclusionsCa_Repleted
###Another Exclusions
ca_pre_post_repletions_MV_new %>%
semi_join(hadm_id_table, by = "hadm_id")
hadm_id_table %>%
semi_join(ca_pre_post_repletions_MV_new, by = "hadm_id")
Analysis and Visualization
We are going to do analysis here on the dataset containing repletions. We are making this distinction, as later on, we will do analysis on non-repletions.
Analysis on Repletions
Table
In this table we are just doing some summary analysis on the dataset.
postExclusionsCa_Repleted %>%
group_by( preVsPost) %>%
summarize(n = n(), mean = mean(valuenum, na.rm = TRUE), standard_deviation = sd(valuenum, na.rm = T))
`summarise()` ungrouping output (override with `.groups` argument)
Histogram
Histogram of Pre and Post Repletions

Pie Chart

Non - Repletion Analysis
allNonRepletions_ca <- bind_rows(list(pre_repletion = nonRepletLabsPre, post_repletion = nonRepletLabsPost), .id = "preVsPost")
#Run Exclusions
allNonRepletions_ca %>%
anti_join(hadm_id_table, by = "hadm_id") -> postexclusion_nonRepletions
postexclusion_nonRepletions
Table
postexclusion_nonRepletions %>%
group_by( preVsPost) %>%
summarize(n = n(), mean = mean(valuenum, na.rm = TRUE), standard_deviation = sd(valuenum, na.rm = T))
Combining the repleted vs NonRepleted pre lab values for comparison
repletedVsNonRepleted_PreV <- bind_rows(list(repleted = postExclusionsCa_Repleted, nonRepleted = postexclusion_nonRepletions), .id = "repleteVsNon") %>% filter(preVsPost == "pre_repletion")
repletedVsNonRepleted_PreV %>% distinct()
Table
#Table
repletedVsNonRepleted_PreV %>%
group_by(repleteVsNon,flag) %>%
summarize(observations = n(),mean = mean(valuenum, na.rm = T), std_dev = sd(valuenum, na.rm=T)) %>%
mutate(percentage = observations / sum(observations) * 100)
Visualization
#Visualizing the non-repleted vs repleted lab values
ggpar(gghistogram(data = repletedVsNonRepleted_PreV, x="valuenum", fill = "flag", add="mean",position = "identity", palette = c("#00AFBB", "#E7B800"),add_density = FALSE, bins = 80, gggtheme = theme_pubr(), xlab = "Lab Value", title = "Pre Vs Post Repletion Lab Values - Potassium", ylab = "Repletions", facet.by = "repleteVsNon")
, xlim = c(0.5,2))
Question: How can there be histogram bars that are “abnormal” on top of bars that are white? Are they just not granular enough to differentiate them? I think there are errors in how the data was flagged. Should look more into this. #### Looking at the outliers
Repleted above the threshold.
repletedVsNonRepleted_PreV %>%
filter(repleteVsNon == "repleted") %>%
filter(flag == "abnormal" & valuenum > 4) %>%
ungroup()%>%
summarize(observations = n(),mean = mean(valuenum, na.rm = T), std_dev = sd(valuenum, na.rm=T))
Didn’t replete Below the threshold.
Table
postexclusion_nonRepletions %>%
filter(flag == "abnormal" & valuenum < 4) %>%
ungroup() %>%
summarize(observations = n(),mean = mean(valuenum, na.rm = T), std_dev = sd(valuenum, na.rm=T))
Visualization of not repletiong below the threshold
postexclusion_nonRepletions %>%
filter(valuenum < 3.5 & flag == "abnormal") %>%
ggplot(aes(x = valuenum)) +
geom_histogram(alpha=0.6, position="identity",bins=14, color="black") + scale_x_continuous(limits = c(1,3.5)) + theme_pubr()
###Amount of people that replete (vs Non-Replete) across lab value
repletedVsNonRepleted_PreV %>%
group_by(valuenum, repleteVsNon) %>%
summarize(observations = n() ) %>%
ggplot(aes(fill=repleteVsNon, y=observations, x=valuenum)) +
geom_bar(position="fill", stat="identity") + scale_x_continuous() + theme_pubr()
Time of Day Analysis
Examining the time of day for each repletion and lab value.
repletedVsNonRepleted_PreV %>%
mutate(RepletionHour = hour(starttime), LabHour = hour(charttime.lab)) %>%
gghistogram(x = c("LabHour","RepletionHour"), bins = 24, palette = "uchicago", alpha = 0.5, merge = T, xlab = "Hours", ylab = "Number of Occurences")
repletedVsNonRepleted_PreV %>%
ungroup() %>%
mutate(LabHour = hour(charttime.lab) + 4, RepletionHour = hour(starttime)) %>%
pivot_longer(cols = c(LabHour,RepletionHour), values_to="Hour", names_to="WhichHour") -> yy
ordered(yy$WhichHour)
yy$WhichHour <- factor(yy$WhichHour, ordered = FALSE)
yy$WhichHour <- relevel(yy$WhichHour,"RepletionHour")
ggplot(yy, aes(x = Hour,fill=WhichHour)) + geom_histogram(position="identity",bins = 24,alpha=0.6) +
scale_x_continuous(breaks=seq(0,24,2)) + theme_pubr()
ggplot(yy, aes(x = Hour,fill=WhichHour)) +
geom_histogram(alpha=0.4, position="identity",bins=24, color="black") + scale_x_continuous(breaks=seq(0,24,2)) + facet_wrap(vars(repleteVsNon))
Pre-Repletion vs Non-Repleted Lab Values
Below, we have the time of day for lab value draws that occur for lab values that were repleted, and those that did not directly lead to a repletion.
It seems that those leading to a repltion were more likely to occur in the morning, while those that did not directly lead to a repletion were more likely to occur in the afternoon.
Why is this occurring? Perhaps, repletions in the morning are routine, but there is no routine in performing repletions in the afternoon.
repletedVsNonRepleted_PreV %>%
ungroup() %>%
mutate(LabHour = hour(charttime.lab)) %>%
filter(preVsPost == "pre_repletion") %>%
ggplot(aes(x = LabHour,fill=repleteVsNon)) + geom_histogram(position="identity",bins = 24,alpha=0.6, color = "black") + scale_x_continuous(breaks=seq(0,24,2)) + theme_pubr() + xlab("Hour of Day") + ylab("Number of Lab Values") + scale_fill_discrete(name = "", labels = c("Non-Repletion", "Repletion"))
Average pre-repletion lab value at each hour
Table
repletedVsNonRepleted_PreV %>%
ungroup() %>%
mutate(LabHour = hour(charttime.lab)) %>%
filter(preVsPost == "pre_repletion") %>%
group_by(LabHour, repleteVsNon) %>%
summarize(mean_lab_value = mean(valuenum, na.rm = T)) %>%
pivot_wider(names_from = repleteVsNon, values_from = mean_lab_value)
Visualization
repletedVsNonRepleted_PreV %>%
ungroup() %>%
mutate(LabHour = hour(charttime.lab)) %>%
filter(preVsPost == "pre_repletion") %>%
group_by(LabHour, repleteVsNon) %>%
summarize(mean_lab_value = mean(valuenum, na.rm = T)) %>%
ggbarplot(x="LabHour", y = "mean_lab_value", fill = "repleteVsNon", facet.by = "repleteVsNon" ) +
scale_x_continuous(breaks=seq(0,24,2)) + theme_pubr()
repletedVsNonRepleted_PreV %>%
ungroup() %>%
mutate(LabHour = hour(charttime.lab)) %>%
filter(preVsPost == "pre_repletion") %>%
group_by(LabHour, repleteVsNon) %>%
summarize(mean_lab_value = mean(valuenum, na.rm = T)) %>%
ggplot(aes(x=LabHour,y=mean_lab_value,group=repleteVsNon, color = repleteVsNon)) + geom_line() + geom_point() + scale_x_continuous(breaks=seq(0,24,2)) + theme_pubr()
repletedVsNonRepleted_PreV %>%
ungroup() %>%
mutate(LabHour = hour(charttime.lab)) %>%
filter(preVsPost == "pre_repletion") %>%
group_by(LabHour, repleteVsNon) %>%
summarize(mean_lab_value = mean(valuenum, na.rm = T)) %>%
ggplot(aes(x=LabHour,y=mean_lab_value,fill=repleteVsNon)) + geom_bar(stat="identity", position = "dodge", color = "black") + scale_x_continuous(breaks=seq(0,24,2)) + theme_pubr()
LS0tCnRpdGxlOiAiQ2EgTm90ZWJvb2siCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCi0tLQp0aXRsZTogIk1pbWljIEVsZWN0cm9seXRlIEFuYWx5c2lzIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICBkZXB0aDogMwogICAgdGhlbWU6IHNpbXBsZXgKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAplZGl0b3Jfb3B0aW9uczoKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKIyMgTG9hZCBMaWJyYXJpZXMKTG9hZCBhbGwgcmVsZXZhbnQgUiBsaWJyYXJpZXMuCmBgYHtyIExvYWQgTGliYXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsICByZXN1bHRzPSdoaWRlJ30KIyBMb2FkIExpYnJhcmllcyAKbGlicmFyeSgiUlBvc3RncmVzIikKbGlicmFyeSgiZ2dwbG90MiIpCmxpYnJhcnkoInRpZHl2ZXJzZSIpCmxpYnJhcnkoImRicGx5ciIpCmxpYnJhcnkoImx1YnJpZGF0ZSIpCmxpYnJhcnkoImRhdGEudGFibGUiKQpsaWJyYXJ5KCJkdHBseXIiKQpsaWJyYXJ5KCJnZ3B1YnIiKQpsaWJyYXJ5KCJtdVN0YXQiKQpsaWJyYXJ5KCJtdm5vcm10ZXN0IikKbGlicmFyeSgia25pdHIiKQpsaWJyYXJ5KCJrYWJsZUV4dHJhIikKYGBgCgojIyBDb25uZWN0IHRvIFJlbGF0aW9uYWwgRGF0YWJhc2UgCmBgYHtyIENvbm5lY3QgdG8gUmVsYXRpb25hbCBEYXRhYmFzZSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBsb2FkIHRoZSBQb3N0Z3JlU1FMIGRyaXZlcgpkcnYgPC0gZGJEcml2ZXIoIlBvc3RncmVzIikKCiMgY3JlYXRlIGEgY29ubmVjdGlvbiB0byB0aGUgcG9zdGdyZXMgZGF0YWJhc2UKIyBzZXQgdGhlIHNlYXJjaCBwYXRoIHRvIHRoZSBtaW1pY2lpaSBzY2hlbWEKY29uIDwtIGRiQ29ubmVjdChkcnYsIGRibmFtZSA9ICJtaW1pYyIsCiAgICAgICAgICAgICAgICAgaG9zdCA9ICJsb2NhbGhvc3QiLCBwb3J0ID0gNTQzMiwKICAgICAgICAgICAgICAgICB1c2VyID0gIm1vdXNhZ2hhbm5hbSIpCmRiU2VuZFF1ZXJ5KGNvbiwgJ3NldCBzZWFyY2hfcGF0aCB0byBtaW1pY2lpaScpCmBgYAoKIyMjIyBTaG93IGEgbGlzdCBvZiBhbGwgdGhlIHRhYmxlcyBpbiB0aGUgcmVsYXRpb25hbCBkYXRhYmFzZS4gCmBgYHtyfQojIHNob3cgYSBsaXN0IG9mIHRhYmxlcwpkYkxpc3RUYWJsZXMoY29uKQpgYGAKCiMjIyMgRnVuY3Rpb24gZm9yIG1vcmUgZWFzaWx5IHB1bGxpbmcgYSBnaXZlbiB0YWJsZS4KVGhpcyBpcyBhIG1lbW9yeSBjb25zZXJ2YXRpdmUgYXBwcm9hY2hpbmcgZm9yIHBlcmZvcm1pbmcgU1FMIHF1ZXJpZXMgb24gREIuIFdlIGNhbiBwdWxsIHRhYmxlcyBpbiBtZW1vcnkgYW5kIGRvIGNlcnRhaW4gYW5hbHlzZXMgb24gdGhlbSwgd2l0aG91dCBhY3R1YWxseSBoYXZpbmcgdG8gc2F2ZSBhIGdpdmVuIG9iamVjdCBpbnRvIHRoZSBSIGVudmlyb25tZW50LiAKYGBge3J9CiNNZW1vcnkgY29uc2VyYXRpdmUgYXBwcm9hY2hpbmcgZm9yIHBlcmZvcm1pbmcgU1FMIHF1ZXJpZXMgb24gREIgCiMoUmV3cml0ZSB0aGlzIGZ1bmN0aW9uIHRvIGhhbmRsZSBtdWx0aXBsZSBpbnB1dHMgKQp0YmxfbWltaWMgPC0gZnVuY3Rpb24odGFibGUpIHsKICB0YWJsZSA8LSBhcy5jaGFyYWN0ZXIoc3Vic3RpdHV0ZSh0YWJsZSkpCiAgdGJsKGNvbiwgZGJwbHlyOjppbl9zY2hlbWEoIm1pbWljaWlpIiwgdGFibGUpKQp9CmBgYAoKIyMjIENyZWF0ZSBkaWN0aW9uYXJ5IG9mIGRpYWdub3NpcyBjb2RlcwpUaGlzICJkaWN0aW9uYXJ5IiB3aWxsIGNvbnRhaW4gYWxsIG9mIHRoZSBkaWFnbm9zZXMgZXZlbnRzIGluIHRoZSBkYXRhYmFzZSBhc3NvY2lhdGVkIHdpdGggYSBnaXZlbiBob3NwaXRhbCBhZG1pc3Npb24gaWQgKGBoYWRtX2lkYCksIGFsb25nIHdpdGggYSBkZXNjcmlwdGlvbiBvZiB0aGUgaWNkOSBjb2RlLiAKYGBge3J9CiNQdWxsIGRpY3Rpb25hcnkgZm9yIGRpYWdub3NlcwpkX2ljZF9kaWFnbm9zZXMgPC0gdGJsX21pbWljKGRfaWNkX2RpYWdub3NlcykgJT4lCiAgc2VsZWN0KGljZDlfY29kZSwgc2hvcnRfdGl0bGUpCgpkaWFnbm9zZXNfaWNkIDwtdGJsX21pbWljKGRpYWdub3Nlc19pY2QpICU+JQogIHNlbGVjdChoYWRtX2lkLCBpY2Q5X2NvZGUsIHNlcV9udW0pCgojSUNELTkgQ29kZXMgKyB0aGVpciBkZXNjcmlwdGlvbnMKaWNkOV9kaWN0IDwtIGRpYWdub3Nlc19pY2QgJT4lCiAgaW5uZXJfam9pbihkX2ljZF9kaWFnbm9zZXMsIGJ5ID0gImljZDlfY29kZSIpCgppY2Q5X2RpY3QKYGBgCiMjIExvYWQgRWxlY3Ryb2x5dGUgTGFiIE1lYXN1cmVtZW50cwoKIyMjIERldGVybWluZSB0aGUgaWRzIGNvcnJlc3BvbmRpbmcgdG8gbGFiIHZhbHVlcyBmb3IgZWFjaCBlbGVjdHJvbHl0ZSAKU2F2ZWQgYWxsIG9mIHRoZSBsYWJfaWRzIGludG8gUiBlbnZpcm9ubWVudCB3aXRoIGBjb2xsZWN0KClgLCBmb3IgZmlsdGVyaW5nIGluIHRoZSBuZXh0IHN0ZXAuCmBgYHtyfQpkX2xhYml0ZW1zIDwtIHRibF9taW1pYyhkX2xhYml0ZW1zKSAjUHVsbCBkaWN0aW9uYXJ5IGZvciBsYWIgaXRlbXMgaW50byBtZW1vcnkgCgpkX2xhYml0ZW1zICU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KHRvbG93ZXIobGFiZWwpLCAiY2FsY2l1bSIpKSAlPiUKICBjb2xsZWN0KCkgLT4gbGFiX2lkc0NhCgpsYWJfaWRzQ2EKYGBgCiMjIyMgRmlsdGVyIHRoZW0gCldlIGFyZSBjaG9vc2luZyB0byBpbmNsdWRlIHRoZSAxc3QgYW5kIDR0aCByb3csIHdoaWNoIGFyZSBzdHJpY3RseSBibG9vZCBsYWIgdmFsdWUgZHJhd3MgZm9yIHBvdGFzc2l1bS4gCmBgYHtyfQojRHJ1Z3MgdG8gaW5jbHVkZSBmb3IgcG90YXNzaXVtIAppdGVtaWRzQ2EgPC0gbGFiX2lkc0NhJGl0ZW1pZFtjKDQpXQpgYGAKIyMjIFB1bGwgdGFibGUgY29udGFpbmluZyBhbGwgbGFiIG1lYXN1cmVtZW50cy4KV2UgYXJlIG5vdyB0YWtpbmcgdGhlIHRhYmxlIGBsYWJldmVudHNgLCB3aGljaCBjb250YWlucyBhbGwgdGhlIGxhYiBldmVudHMgaW4gdGhlIGRhdGFiYXNlLCBhbmQgc2VsZWN0aW5nIHRoZSByb3dzIGFzc29jaWF0ZWQgd2l0aCB0aGUgdHdvIGJsb29kIHBvdGFzc2l1bSBsYWIgdmFsdWVzIGBpdGVtaWRgcyB0aGF0IHdlIGNob3NlIGFib3ZlLiAKYGBge3J9CmxhYmV2ZW50cyA8LSB0YmxfbWltaWMobGFiZXZlbnRzKQoKbGFiZXZlbnRzICU+JSAKICBmaWx0ZXIoaXRlbWlkICVpbiUgaXRlbWlkc0NhKSAgJT4lCiAgaW5uZXJfam9pbihzZWxlY3QodGJsX21pbWljKGRfbGFiaXRlbXMpLC1sb2luY19jb2RlLCAtcm93X2lkICksIGJ5ID0gIml0ZW1pZCIpICU+JQogIHNlbGVjdChzdWJqZWN0X2lkLCBoYWRtX2lkLCBpdGVtaWQsIGNoYXJ0dGltZSwgdmFsdWVudW0sIHZhbHVldW9tLCBsYWJlbCwgZmxhZywgZmx1aWQsIGNhdGVnb3J5KSAtPiBsYWJFdmVudHNDYQoKbGFiRXZlbnRzQ2EgJT4lIGNvdW50KCkgJT4lIGNvbGxlY3QoKSAtPiBudW1MYWJDYWV2ZW50cwoKbGFiRXZlbnRzQ2EKCnByaW50KHBhc3RlKCIgVGhlcmUgYXJlICIscHVsbChudW1MYWJDYWV2ZW50c1sxLDFdKSwgIiB0b3RhbCBtYWduZXNpdW0gbGFiIHZhbHVlcyB0aGF0IHdlcmUgZXh0cmFjdGVkIGZyb20gdGhlIGxhYmV2ZW50cyB0YWJsZS4iICkpCgpgYGAKIyMgRGVmaW5lIHJlcGxldGlvbiBldmVudHMgd2l0aGluIHRoZSBtZXRhdmlldyBkYXRhYmFzZQpUaGUgYGRfaXRlbXNgIHRhYmxlIGluIG1pbWljcyBjb250YWlucyBhIGRpY3Rpb25hcnkgZm9yIGFsbCBvZiB0aGUgZXZlbnRzIGFzc29jaWF0ZWQgd2l0aCBib3RoIHRoZSBtZXRhdmlldyBhbmQgY2FyZXZpZXcgZGF0YWJhc2UuIFdlIGFyZSBvbmx5IGdvaW5nIHRvIGZvY3VzIG9uIHRoZSBtZXRhdmlldyBkYXRhYmFzZSBhdCB0aGlzIHBvaW50LiBXZSB0aGVuIHdhbnQgdG8gZmlsdGVyIHRoaXMgdGFibGUgYnkgdGhlIGxhYmVsIGRlc2NyaXB0aW9uIGZvciBpdGVtcyB0aGF0IGFyZSByZWxhdGVkIHRvIHBvdGFzc2l1bS4gCmBgYHtyfQpkX2l0ZW1zIDwtIHRibF9taW1pYyhkX2l0ZW1zKSAjUHVsbCBkaWN0aW9uYXJ5IGZvciBhbGwgbGFiIGV2ZW50cyBpbnRvIG1lbW9yeSAKCmRfaXRlbXMgJT4lCiAgZmlsdGVyKGxhYmVsICVpbGlrZSUgIiVjYWxjaXVtJSIgKSAlPiUKICBmaWx0ZXIoc3RyX2RldGVjdCh0b2xvd2VyKGRic291cmNlKSwgIm1ldGEiKSkgJT4lCiAgYXJyYW5nZShsYWJlbCkgLT4gY2FfaXRlbXNfTVYKCmNhX2l0ZW1zX01WIAoKYGBgCkFsdGhvdWdoIHRoZXJlIGFyZSBldmVudHMgc291cmNlZCBmcm9tIGVpdGhlciBtZXRhdmlldyBvciBjYXJldmlldywgdGhlc2UgdHdvIGhvc3BpdGFsIGRhdGFiYXNlcyBhcmUgYWN0dWFsbHkgbGlua2VkIHRvIDMgZGlmZmVyZW50IHRhYmxlcyBpbiB0aGUgbWltaWNzIGRhdGFiYXNlOiBgaW5wdXRldmVudHNfbXZgLCBgaW5wdXRldmVudHNfY3ZgIG9yIGBjaGFydGV2ZW50cy5gIFNpbmNlIHdlIGFyZSBleGFtaW5pbmcgb25seSB0aGUgbWV0YXZpc2lvbiBkYXRhYmFzZSwgaXQgd2lsbCBvbmx5IGxpbmsgdG8gYGlucHV0ZXZlbnRzX212YCBhbmQgYGNoYXJ0ZXZlbnRzLmAgV2Ugd291bGQgbGlrZSB0byBzZWxlY3QgZm9yIG9ubHkgdGhlIGV2ZW50cyB0aGF0IGxpbmsgdG8gIGBpbnB1dGV2ZW50c19tdmAuCgpgYGB7cn0KY2FfaXRlbXNfTVYgJT4lIAogIGZpbHRlcihsaW5rc3RvPT0iaW5wdXRldmVudHNfbXYiKSAlPiUgY29sbGVjdCgpIC0+IGNhX2l0ZW1zX01WX0lNCgpjYV9pdGVtc19NVl9JTQpgYGAKSWYgeW91IGxvb2sgYXQgdGhlIGFib3ZlIHRhYmxlLCB0aGUgZmlyc3Qgcm93IGhhcyBhIEtDTCAoQm9sdXMpIHdpdGggdW5pdCBtTCwgd2hpbGUgdGhlIDV0aCByb3cgaGFzIFBvdGFzc2l1bSBDaGxvcmlkZSB3aXRoIHVuaXRzIGluIG1FcS4gQWNjb3JkaW5nIHRvIHRoZSBtaW1pY3MgaWlpIHdlYnNpdGU6IAoKPiAiTWV0YXZpc2lvbiByZWNvcmRzIElPIGRhdGEgdXNpbmcgdHdvIHRhYmxlczogUkFOR0VTSUdOQUxTIGFuZCBPUkRFUkVOVFJZLiBUaGVzZSB0YWJsZXMgZG8gbm90IGFwcGVhciBpbiBNSU1JQy1JSUkgYXMgdGhleSBoYXZlIGJlZW4gbWVyZ2VkIHRvIGZvcm0gdGhlID4gSU5QVVRFVkVOVFNfTVYgdGFibGUuIFJBTkdFU0lHTkFMUyBjb250YWlucyByZWNvcmRlZCBkYXRhIGVsZW1lbnRzIHdoaWNoIGxhc3QgZm9yIGEgZml4ZWQgcGVyaW9kIG9mIHRpbWUuIEZ1cnRoZXJtb3JlLCB0aGUgUkFOR0VTSUdOQUxTIHRhYmxlIHJlY29yZGVkID4gaW5mb3JtYXRpb24gZm9yIGVhY2ggY29tcG9uZW50IG9mIHRoZSBkcnVnIHNlcGFyYXRlbHkuIEZvciBleGFtcGxlLCBmb3IgYSBub3JlcGluZXBocmluZSBhZG1pbmlzdHJhdGlvbiB0aGVyZSB3b3VsZCBiZSB0d28gY29tcG9uZW50czogYSBtYWluIG9yZGVyICAgID4gY29tcG9uZW50IChub3JlcGluZXBocmluZSkgYW5kIGEgc29sdXRpb24gY29tcG9uZW50IChOYUNsKS4iIFteMV0KClteMV06IDxlbT5odHRwczovL21pbWljLnBoeXNpb25ldC5vcmcvbWltaWNkYXRhL2lvLzwvZW0+IAoKVGhlcmVmb3JlLCB3ZSB3aWxsIG9ubHkgYmUgc2VsZWN0aW5nIHJlcGxldGlvbnMgYmFzZWQgb2ZmIG9mIHRoZSAibWFpbiBvcmRlciBjb21wb25lbnQiLCBhbmQgaWdub3JpbmcgdGhlIHNvbHV0aW9uLiBXZSB3aWxsIHNlbGVjdCBmb3IgdGhlIDR0aCBhbmQgNXRoIHJvd3MsIHdoaWNoIGNvcnJlc3BvbmQgdG8gUG90YXNzaXVtZSBBY2V0YXRlIGFuZCBQb3Rhc3NpdW0gQ2hsb3JpZGUuIAoKYGBge3J9CmNhX2l0ZW1zX01WX0lNICU+JSBzbGljZSgxKSAtPiBjYV9pdGVtc19NVl9JTSAjT05MWSBLRUVQSU5HIFRIRSBSRVBMRVRJT05TLCBUSEUgQURESVRJVkUgQU1PVU5UUywgTk9UIFRIRSBCT0xVUyAKY2FfaXRlbXNfTVZfSU0KCmNhX2l0ZW1zX01WX0lNICU+JSBwdWxsKGl0ZW1pZCkgLT4gY2FfaXRlbXNfTVZfSU1fdmVjdG9yIApgYGAKIyMgUHVsbCByZXBsZXRpb24gZXZlbnRzIGZyb20gbWV0YXZpZXcgZGF0YWJzZQpXZSB3aWxsIG5vdyB1c2UgdGhlIGZpbHRlcmVkIGlkcyBmb3IgdGhlIHR3byBhZGRpdGl2ZSBzb2x1dGlvbnMgYWJvdmUgdG8gcHVsbCBlbGVjdHJvbHl0ZSByZXBsZXRpb25zIGZyb20gdGhlIGBpbnB1dGV2ZW50c19tdmAgdGFibGUuCmBgYHtyfQppbnB1dGV2ZW50c19tdiA8LSB0YmxfbWltaWMoaW5wdXRldmVudHNfbXYpCgppbnB1dGV2ZW50c19tdiAlPiUKICBmaWx0ZXIoaXRlbWlkICVpbiUgY2FfaXRlbXNfTVZfSU1fdmVjdG9yKSAlPiUKICBzZWxlY3Qoc3ViamVjdF9pZCwgaGFkbV9pZCxpY3VzdGF5X2lkLCBsaW5rb3JkZXJpZCwgb3JkZXJpZCwgaXRlbWlkLCBzdGFydHRpbWUsIGVuZHRpbWUsIGFtb3VudCwgYW1vdW50dW9tLCByYXRlLCByYXRldW9tLCBzdGF0dXNkZXNjcmlwdGlvbiwgb3JkZXJjb21wb25lbnR0eXBlZGVzY3JpcHRpb24pICU+JQogIGZpbHRlcighc3RhdHVzZGVzY3JpcHRpb24gPT0gIlJld3JpdHRlbiIpICU+JQogIHJlbmFtZShpdGVtaWQucmVwbGV0aW9uID0gaXRlbWlkKSAtPiByZXBFdmVudHNDYQoKcmVwRXZlbnRzQ2EKYGBgCkRldGVybWluZSB0aGUgYW1vdW50IG9mIHBvdGFzc2l1bSByZXBsZXRpb25zOiAKYGBge3IgUG90YXNzaXVtIFJlcGxldGlvbiBDb3VudH0KcmVwRXZlbnRzQ2EgJT4lIGNvdW50KCkgJT4lIGNvbGxlY3QoKSAtPiBudW1DYVJlcGxldGlvbnMKCnByaW50KHBhc3RlKCIgVGhlcmUgYXJlICIscHVsbChudW1DYVJlcGxldGlvbnNbMSwxXSksICIgdG90YWwgcG90YXNzaXVtIHJlcGxldGlvbnMgdGhhdCB3ZXJlIGV4dHJhY3RlZCBmcm9tIHRoZSBpbnB1dGV2ZW50c19tdiB0YWJsZS4iICkpCgpgYGAKCiMjIEpvaW4gdGFibGVzIGNvbnRhaW5pbmcgcmVwbGV0aW9uIGV2ZW50cyBhbmQgbGFiIGV2ZW50cyAKQm90aCB0aGUgdGFibGVzIGNvbnRhaW5pbmcgdGhlIGxhYiBkcmF3cyBhbmQgdGhlIHJlcGxldGlvbiBldmVudHMgaGF2ZSBiZWVuIGV4dHJhY3RlZC4gV2UgY2FuIG5vdyBqb2luIHRoZW0gdG8gYmVnaW4gYW5hbHl6aW5nIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGVtLiAKCkluIGFkZGl0aW9uIHRvIGpvaW5pbmcgdGhlIHRhYmxlcywgd2UgaGF2ZSByZW5hbWVkIHRoZSBjb2x1bW4gY29ycmVzcG9uZGluZyB0byB0aGUgbW9tZW50IHRoZSBmbHVpZCBjb2xsZWN0aW9uIGZvciB0aGUgbGFiIHZhbHVlIHdhcyByZWNvcmRlZCwgYGNoYXJ0dGltZWAsIGFzIGBjaGFydHRpbWUubGFiYCBmb3IgY2xhcml0eS4gCmBgYHtyIEpvaW4gUmVwbGV0aW9ucyBhbmQgTGFiIEV2ZW50c30KcmVwRXZlbnRzQ2EgJT4lIAogIGlubmVyX2pvaW4obGFiRXZlbnRzQ2EsIGJ5PWMoInN1YmplY3RfaWQiID0gInN1YmplY3RfaWQiLCAiaGFkbV9pZCIgPSAiaGFkbV9pZCIpKSAlPiUKICBkaXN0aW5jdCgpICU+JQogIHJlbmFtZShjaGFydHRpbWUubGFiID0gY2hhcnR0aW1lKSAlPiUKICBjb2xsZWN0KCkgLT4gY2FfbGFiX3JlcGxldGlvbnNfTVZfbmV3CgpjYV9sYWJfcmVwbGV0aW9uc19NVl9uZXcKYGBgCiMjIyBGaW5kIHJlY2VudCByZXBsZXRpb25zCldlIHdhbnQgdG8gZmxhZyBhbGwgcmVwbGV0aW9uIGV2ZW50cyB0aGF0IG9jY3VycmVkIGVpdGhlciAyNCBob3VycyBiZWZvcmUgb3IgMjQgaG91cnMgYWZ0ZXIgYSBnaXZlbiBsYWIgdmFsdWUuIFdlIHRoZW4gZmlsdGVyIHRoZSB0YWJsZSwgc3RvcmluZyBhbGwgb2YgdGhlIHJlcGxldGlvbnMgb2NjdXJyaW5nIEJFRk9SRSBhIGdpdmVuIGxhYiB2YWx1ZSBpbiBvbmUgdGFibGUsIGFuZCBhbGwgb2YgdGhlIG9uZXMgb2NjdXJyaW5nIEFGVEVSIGEgZ2l2ZW4gbGFiIHZhbHVlIGluIGFub3RoZXIuIApgYGB7cn0KI0ZpbmQgdGhlIFJFQ0VOVCBSZXBsZXRpb25zCmNhX2xhYl9yZXBsZXRpb25zX01WX25ldyAlPiUKICBtdXRhdGUoY2hhcnR0aW1lLmxhYj0gYXNfZGF0ZXRpbWUoY2hhcnR0aW1lLmxhYiksIGVuZHRpbWU9YXNfZGF0ZXRpbWUoZW5kdGltZSksIHN0YXJ0dGltZSA9IGFzX2RhdGV0aW1lKHN0YXJ0dGltZSkpICU+JQogIG11dGF0ZShpc1JlY2VudFByZSA9IGRpZmZ0aW1lKHN0YXJ0dGltZSwgY2hhcnR0aW1lLmxhYiwgdW5pdHMgPSAiaG91cnMiKSA8PSAyNCAmIGRpZmZ0aW1lKHN0YXJ0dGltZSwgY2hhcnR0aW1lLmxhYiwgdW5pdHMgPSAiaG91cnMiKSA+IDAgKSAlPiUKICBtdXRhdGUoaXNSZWNlbnRQb3N0ID0gZGlmZnRpbWUoZW5kdGltZSwgY2hhcnR0aW1lLmxhYiwgdW5pdHMgPSAiaG91cnMiKSA+PSAtMjQgJiBkaWZmdGltZShlbmR0aW1lLCBjaGFydHRpbWUubGFiLCB1bml0cyA9ICJob3VycyIpIDwgMCApICAtPiBhbGxSZXBMYWJFdmVudHNfY2EKCmFsbFJlcExhYkV2ZW50c19jYSAlPiUgCiAgZmlsdGVyKGlzUmVjZW50UHJlIHwgaXNSZWNlbnRQb3N0KQpgYGAKCiMjIyBFeHRyYWN0IHRoZSBtb3N0IHJlY2VudCByZXBsZXRpb24sIHByaW9yIGFuZCBhZnRlciBhIGdpdmVuIGxhYiB2YWx1ZS4gCldlIGhhdmUgYSB0YWJsZSB0aGF0IGNvbnRhaW5zIGFsbCBvZiB0aGUgcmVwbGV0aW9uIGV2ZW50cyB0aGF0IG9jY3VyIGVpdGhlciAyNCBob3VycyBwcmlvciBvciBhZnRlciBhIGdpdmVuIGxhYiB2YWx1ZS4gV2UgdGhlbiBncm91cCB0aGUgcm93cyBieSBlYWNoIHN1YmplY3QgYW5kIGhvc3BpdGFsIGlkLCBhcyB3ZWxsIGFzIHRoZSB0aW1lIG9mIGZsdWlkIGFjcXVpc2l0aW9uIGZvciB0aGUgbGFiIHZhbHVlLiBUaGVuLCB3ZSBjcmVhdGUgYSBjb2x1bW4gdGhhdCBnZW5lcmF0ZXMgYSBmbGFnIGlmIHRoZSBsYWIgdmFsdWUgdGhhdCBpcyBjaG9zZW4gaXMgdGhlIG1pbmltdW0gYHN0YXJ0dGltZWAgZm9yIGFsbCB0aGUgcG9zc2libGUgYHN0YXJ0dGltZWBzLiBCZWNhdXNlIHRoZXJlIGFyZSBtdWx0aXBsZSBsYWIgdmFsdWVzLCBhcyB3ZWxsIGFzIG11bHRpcGxlIHJlcGxldGlvbiBldmVudHMsIGZvciBhIGdpdmVuIGBoYWRtX2lkYCB0aGUgdGFibGUgb3V0cHV0dGVkIGhhcyBhbGwgdGhlIHBvc3NpYmxlIHBlcm11dGF0aW9ucy4gV2Ugd2FudCB0byBzZWxlY3QgdGhlIG1vc3QgcmVjZW50IHJlcGxldGlvbiBBRlRFUiBhIGxhYiB2YWx1ZSwgc28gd2UgY2hvb3NlIHRoZSByZXBsZXRpb24gd2l0aCB0aGUgYHN0YXJ0dGltZWAgZXF1YWwgdG8gdGhlIG1pbmltdW0gYHN0YXJ0dGltZWAgZm9yIGEgZ2l2ZW4gbGFiIHZhbHVlLiAKCmBgYHtyfQojUG90YXNzaXVtIGxhYiBldmVudCBQUkUtcmVwbGV0aW9ucyAKYWxsUmVwTGFiRXZlbnRzX2NhICU+JQogIGZpbHRlcihpc1JlY2VudFByZSkgJT4lIAogIGdyb3VwX2J5KHN1YmplY3RfaWQsIGhhZG1faWQsY2hhcnR0aW1lLmxhYikgJT4lCiAgbXV0YXRlKGlzTW9zdFJlY2VudFJlcGxldGlvbiA9IHN0YXJ0dGltZSA9PSBtaW4oc3RhcnR0aW1lKSkgJT4lCiAgZmlsdGVyKGlzTW9zdFJlY2VudFJlcGxldGlvbikgJT4lCiAgdW5ncm91cCgpICU+JSAKICBncm91cF9ieShzdWJqZWN0X2lkLCBoYWRtX2lkLCBzdGFydHRpbWUpICU+JQogIG11dGF0ZShpc01vc3RSZWNlbnRMYWJFdmVudCA9IGNoYXJ0dGltZS5sYWIgPT0gbWF4KGNoYXJ0dGltZS5sYWIpKSAlPiUKICBmaWx0ZXIoaXNNb3N0UmVjZW50TGFiRXZlbnQpICAlPiUgZGlzdGluY3QoKSAtPiBwcmVfY2FfbGFiX3JlcGxldGlvbnNfTVZfbmV3CgpwcmVfY2FfbGFiX3JlcGxldGlvbnNfTVZfbmV3CmBgYAoKRm9yIHRoZSBwb3N0LXJlcGxldGlvbiBsYWIgdmFsdWVzLCB3ZSBhcmUgbm93IGRvaW5nIHRoZSBzYW1lIHRoaW5nLCBidXQgaW5zdGVhZCBmaW5kaW5nIHRoZSBtYXhpbXVtIGVuZHRpbWUgcHJpb3IgdG8gYSBnaXZlbiBsYWIgdmFsdWUsIHRvIHNlbGVjdCB0aGUgbW9zdCByZWNlbnQgcmVwbGV0aW9uIHByaW9yIHRvIGEgZ2l2ZW4gbGFiIHZhbHVlLiAKCmBgYHtyfQojUG90YXNzaXVtIGxhYiBldmVudCBQT1NULXJlcGxldGlvbnMgCmFsbFJlcExhYkV2ZW50c19jYSAlPiUKICBmaWx0ZXIoaXNSZWNlbnRQb3N0KSAlPiUgCiAgZ3JvdXBfYnkoc3ViamVjdF9pZCwgaGFkbV9pZCxjaGFydHRpbWUubGFiKSAlPiUKICBtdXRhdGUoaXNNb3N0UmVjZW50UmVwbGV0aW9uID0gZW5kdGltZSA9PSBtYXgoZW5kdGltZSkpICU+JQogIGZpbHRlcihpc01vc3RSZWNlbnRSZXBsZXRpb24pICU+JQogIHVuZ3JvdXAoKSAlPiUgCiAgZ3JvdXBfYnkoc3ViamVjdF9pZCwgaGFkbV9pZCwgZW5kdGltZSkgJT4lCiAgbXV0YXRlKGlzTW9zdFJlY2VudExhYkV2ZW50ID0gY2hhcnR0aW1lLmxhYiA9PSBtaW4oY2hhcnR0aW1lLmxhYikpICU+JQogIGZpbHRlcihpc01vc3RSZWNlbnRMYWJFdmVudCkgICU+JSBkaXN0aW5jdCgpIC0+IHBvc3RfY2FfbGFiX3JlcGxldGlvbnNfTVZfbmV3Cgpwb3N0X2NhX2xhYl9yZXBsZXRpb25zX01WX25ldwpgYGAKCgpXZSB0aGVuIGNvbWJpbmUgYWxsIG9mIHRoZSB2YWx1ZXMgZnJvbSB0aGUgInByZS1yZXBsZXRpb24iIGFuZCAicG9zdC1yZXBsZXRpb24iIHRhYmxlcywgaW50byBvbmUgdGFibGUuIFdlIGNyZWF0ZSBhIG5ldyBjb2x1bW4gYHByZVZzUG9zdGAgdG8gaW5kaWNhdGUgaWYgYSBnaXZlbiBldmVudCBpcyBlaXRoZXIgYmVmb3JlIG9yIGFmdGVyIGEgbGFiIHZhbHVlLiBDb25jZWl2YWJseSwgdGhlIHNhbWUgcmVwbGV0aW9uIGNvdWxkIGNvcnJlc3BvbmQgdG8gYSAKCmBgYHtyfQojQ29tYmluZSB0aGVtIGludG8gb25lIGRhdGFzZXQgCmNhX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3IDwtIGJpbmRfcm93cyhsaXN0KHByZV9yZXBsZXRpb24gPSBwcmVfY2FfbGFiX3JlcGxldGlvbnNfTVZfbmV3LCBwb3N0X3JlcGxldGlvbiA9IHBvc3RfY2FfbGFiX3JlcGxldGlvbnNfTVZfbmV3KSwgLmlkID0gInByZVZzUG9zdCIpCmNhX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3ICU+JSBkaXN0aW5jdCgpCgpgYGAKCiMjIE5vbiBSZXBsZXRpb24gRXZlbnRzCldlIHdpbGwgbm93IGRldGVybWluZSB0aGUgc2V0IG9mIGxhYiB2YWx1ZXMgZm9yIHdoaWNoIHRoZXJlIHdhcyBubyBpbW1lZGlhdGUgcmVwbGV0aW9uIGFmdGVyd2FyZC4gCgojIyMgRXh0cmFjdCBub24tcmVwbGV0ZWQgdmFsdWVzIHdpdGhpbiAyNCBob3VycwpJbiB0aGlzIHNlY3Rpb24sIHdlIGhhdmUgc3RhcnRlZCBieSBmaWx0ZXJpbmcgYWxsIHRoZSByZXBsZXRpb25zIHByaW9yIGFuZCBhZnRlciBhIGxhYiB2YWx1ZSwganVzdCBsaWtlIGZvciB0aGUgYW5hbHlzaXMgb24gdGhlIHJlcGxldGlvbnMuIEhvd2V2ZXIsIHRoaXMgdGltZSwgaWYgdGhlcmUgYXJlIG11bHRpcGxlIGxhYiB2YWx1ZXMgdGhhdCBvY2N1ciBiZWZvcmUgYSBnaXZlbiByZXBsZXRpb24gKG9yIGFmdGVyKSwgd2UgYXJlIHRha2luZyB0aGUgb25lcyBhZnRlciB0aGUgZmlyc3Qgb25lLiAKCmBgYHtyfQojUG90YXNzaXVtIGxhYiBldmVudCBQUkUtcmVwbGV0aW9ucyAKYWxsUmVwTGFiRXZlbnRzX2NhICU+JQogIGZpbHRlcihpc1JlY2VudFByZSkgJT4lIAogIGdyb3VwX2J5KHN1YmplY3RfaWQsIGhhZG1faWQsY2hhcnR0aW1lLmxhYikgJT4lCiAgbXV0YXRlKGlzTW9zdFJlY2VudFJlcGxldGlvbiA9IHN0YXJ0dGltZSA9PSBtaW4oc3RhcnR0aW1lKSkgJT4lCiAgZmlsdGVyKGlzTW9zdFJlY2VudFJlcGxldGlvbikgJT4lCiAgdW5ncm91cCgpICU+JSAKICBncm91cF9ieShzdWJqZWN0X2lkLCBoYWRtX2lkLCBzdGFydHRpbWUpICU+JQogIG11dGF0ZShpc01vc3RSZWNlbnRMYWJFdmVudCA9IGNoYXJ0dGltZS5sYWIgPT0gbWF4KGNoYXJ0dGltZS5sYWIpKSAlPiUKICBmaWx0ZXIoIWlzTW9zdFJlY2VudExhYkV2ZW50KSAgJT4lIGRpc3RpbmN0KCkgLT4gbm9uUmVwbGV0TGFic1ByZSAKCiNQb3Rhc3NpdW0gbGFiIGV2ZW50IFBPU1QtcmVwbGV0aW9ucyAKYWxsUmVwTGFiRXZlbnRzX2NhICU+JQogIGZpbHRlcihpc1JlY2VudFBvc3QpICU+JSAKICBncm91cF9ieShzdWJqZWN0X2lkLCBoYWRtX2lkLGNoYXJ0dGltZS5sYWIpICU+JQogIG11dGF0ZShpc01vc3RSZWNlbnRSZXBsZXRpb24gPSBlbmR0aW1lID09IG1heChlbmR0aW1lKSkgJT4lCiAgZmlsdGVyKGlzTW9zdFJlY2VudFJlcGxldGlvbikgJT4lCiAgdW5ncm91cCgpICU+JSAKICBncm91cF9ieShzdWJqZWN0X2lkLCBoYWRtX2lkLCBlbmR0aW1lKSAlPiUKICBtdXRhdGUoaXNNb3N0UmVjZW50TGFiRXZlbnQgPSBjaGFydHRpbWUubGFiID09IG1pbihjaGFydHRpbWUubGFiKSkgJT4lCiAgZmlsdGVyKCFpc01vc3RSZWNlbnRMYWJFdmVudCkgICU+JSBkaXN0aW5jdCgpIC0+IG5vblJlcGxldExhYnNQb3N0CgphbGxOb25SZXBsZXRpb25zX2NhIDwtIGJpbmRfcm93cyhsaXN0KHByZV9yZXBsZXRpb24gPSBub25SZXBsZXRMYWJzUHJlLCBwb3N0X3JlcGxldGlvbiA9IG5vblJlcGxldExhYnNQb3N0KSwgLmlkID0gInByZVZzUG9zdCIpCmBgYAoKCiMjIEV4Y2x1c2lvbnMgCldlIG5vdyB3YW50IHRvIGV4Y2x1ZGUgYWxsIHRoZSByb3dzIGJhc2VkIG9uIGNlcnRhaW4gY3JpdGVyaWEuIAoKV2UgaGF2ZSBhIHRhYmxlIGNhbGxlZCBgaGFkbV9pZF90YWJsZWAgd2hpY2ggY29udGFpbnMgYWxsIG9mIHRoZSBoYWRtX2lkcyBvZiBwYXRpZW50cyB3aXRoIGRpYWdub3NlcyB0aGF0IHdvdWxkIGNvbmZvdW5kIG91ciByZXN1bHRzIChraWRuZXkgZGlzZWFzZSwgZXRjKS4gKFdJTEwgQUREIEhPVyBJIENSRUFURUQgVEhJUyBUQUJMRSBMQVRFUikuCgpgYGB7cn0KaGFkbV9pZF90YWJsZQpgYGAKCkFzIGEgcmVzdWx0LCB3ZSBjb3VsZCBzaW1wbHkgZG8gYSBzZW1pLWpvaW4gdG8gZGV0ZXJtaW5lIGhvdyBtYW55IHBlb3BsZSBpbiBlYWNoIGdyb3VwIHdlcmUgZXhjbHVkZWQuICoqTm90ZSoqLCB3ZSBhcmUgZG9pbmcgdGhlIGFudGktam9pbiBvbiB0aGUgdGFibGUgd2l0aCB0aGUgbW9zdCByZWNlbnQgcmVwbGV0aW9uIGJlZm9yZSBhbmQgYWZ0ZXIgYSBnaXZlbiBsYWIgcmVzdWx0LCB3aXRoaW4gYSAyNCBob3VyIHRpbWUgcmFuZ2UuIFdlIGFyZSAqKm5vdCoqIGRvaW5nIGl0IG9uIHRoZSBzZXQgb2YgQUxMIHBvc3NpYmxlIHJlcGxldGlvbiBhbmQgb3IgbGFiIHJlc3VsdCBldmVudHMuIAoKYGBge3J9CmhhZG1faWRfdGFibGUgJT4lCiAgc2VtaV9qb2luKGNhX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3LCBieSA9ICJoYWRtX2lkIikgJT4lIAogIHNlbGVjdChoYWRtX2lkLCAuaWQpICU+JQogIGdyb3VwX2J5KC5pZCkgJT4lCiAgc3VtbWFyaXplKG4gPSBuKCkpICU+JSAKICBhcnJhbmdlKGRlc2MobikpICU+JQogIGthYmxlKCkKCiMgY2FfcHJlX3Bvc3RfcmVwbGV0aW9uc19NVl9uZXcgJT4lCiMgICB1bmdyb3VwKCklPiUKIyAgIHNlbWlfam9pbihoYWRtX2lkX3RhYmxlLCBieSA9ICJoYWRtX2lkIikgJT4lIAojICAgc2VsZWN0KGljdXN0YXlfaWQsIG9yZGVyaWQsIC5pZCkgJT4lCiMgICBncm91cF9ieSguaWQpICU+JQojICAgc3VtbWFyaXplKG4gPSBuKCkpICU+JSAKIyAgIGFycmFuZ2UoZGVzYyhuKSkgJT4lCiMgICBrYWJsZSgpCmBgYAoKQW5kIGhlcmUgaXMgdGhlIHJlc3VsdGFudCBkYXRhc2V0IGFmdGVyICphY3R1YWxseSogZXhjbHVkaW5nIHRoaXMgdGltZSwgdXNpbmcgYW4gYW50aS1qb2luIGluc3RlYWQuIApgYGB7cn0KY2FfcHJlX3Bvc3RfcmVwbGV0aW9uc19NVl9uZXcgJT4lCiAgYW50aV9qb2luKGhhZG1faWRfdGFibGUsIGJ5ID0gImhhZG1faWQiKSAtPiBwb3N0RXhjbHVzaW9uc0NhX1JlcGxldGVkCgpwb3N0RXhjbHVzaW9uc0NhX1JlcGxldGVkCmBgYAoKIyMjQW5vdGhlciBFeGNsdXNpb25zIApgYGB7cn0KY2FfcHJlX3Bvc3RfcmVwbGV0aW9uc19NVl9uZXcgJT4lCiAgc2VtaV9qb2luKGhhZG1faWRfdGFibGUsIGJ5ID0gImhhZG1faWQiKQoKaGFkbV9pZF90YWJsZSAlPiUKICBzZW1pX2pvaW4oY2FfcHJlX3Bvc3RfcmVwbGV0aW9uc19NVl9uZXcsIGJ5ID0gImhhZG1faWQiKQpgYGAKCgojIyBBbmFseXNpcyBhbmQgVmlzdWFsaXphdGlvbiAKV2UgYXJlIGdvaW5nIHRvIGRvIGFuYWx5c2lzIGhlcmUgb24gdGhlIGRhdGFzZXQgY29udGFpbmluZyByZXBsZXRpb25zLiBXZSBhcmUgbWFraW5nIHRoaXMgZGlzdGluY3Rpb24sIGFzIGxhdGVyIG9uLCB3ZSB3aWxsIGRvIGFuYWx5c2lzIG9uIG5vbi1yZXBsZXRpb25zLiAKCiMjIyBBbmFseXNpcyBvbiBSZXBsZXRpb25zCiMjIyMgVGFibGUKSW4gdGhpcyB0YWJsZSB3ZSBhcmUganVzdCBkb2luZyBzb21lIHN1bW1hcnkgYW5hbHlzaXMgb24gdGhlIGRhdGFzZXQuIAoKYGBge3J9CnBvc3RFeGNsdXNpb25zQ2FfUmVwbGV0ZWQgJT4lCiAgZ3JvdXBfYnkoIHByZVZzUG9zdCkgJT4lCiAgc3VtbWFyaXplKG4gPSBuKCksIG1lYW4gPSBtZWFuKHZhbHVlbnVtLCBuYS5ybSA9IFRSVUUpLCBzdGFuZGFyZF9kZXZpYXRpb24gPSBzZCh2YWx1ZW51bSwgbmEucm0gPSBUKSkgCmBgYAoKIyMjIyBIaXN0b2dyYW0KSGlzdG9ncmFtIG9mIFByZSBhbmQgUG9zdCBSZXBsZXRpb25zCmBgYHtyLGZpZy5zdWJjYXA9ICJQcmUgVnMgUG9zdCBSZXBsZXRpb24gTGFiIFZhbHVlcyJ9CmdncGFyKGdnaGlzdG9ncmFtKGRhdGEgPSBwb3N0RXhjbHVzaW9uc0NhX1JlcGxldGVkLCB4PSJ2YWx1ZW51bSIsIGZpbGwgPSAicHJlVnNQb3N0IiwgIGFkZD0ibWVhbiIsIHBhbGV0dGUgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiKSxhZGRfZGVuc2l0eSA9IEZBTFNFLCBiaW5zID0gNDAsIGdnZ3RoZW1lID0gdGhlbWVfcHVicigpLCB4bGFiID0gIkxhYiBWYWx1ZSIsIHRpdGxlID0gIlByZSBWcyBQb3N0IFJlcGxldGlvbiBMYWIgVmFsdWVzIC0gUG90YXNzaXVtIiwgeWxhYiA9ICJSZXBsZXRpb25zIikKICAgICAgLCB4bGltID0gYygwLjUsNC4yKSkKCmdncGxvdChwb3N0RXhjbHVzaW9uc0NhX1JlcGxldGVkLCBhZXMoeCA9IHZhbHVlbnVtLGZpbGw9cHJlVnNQb3N0KSkgKwogIGdlb21faGlzdG9ncmFtKGFscGhhPTAuNiwgcG9zaXRpb249ImlkZW50aXR5IixiaW5zPTc1LCBjb2xvcj0iYmxhY2siKSArIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAuNSwyKSApICsgdGhlbWVfcHVicigpICsgeGxhYigiQ2FsY2l1bSBWYWx1ZSIpICsgeWxhYigiTnVtYmVyIG9mIE9jY3VycmVuY2VzIikgKyBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWUgPSAiIiwgbGFiZWxzID0gYygiUG9zdCByZXBsZXRpb24gQ2EiLCAiUHJlIHJlcGxldGlvbiBDYSIpKQoKYGBgCgojIyMjIFBpZSBDaGFydCAKCmBgYHtyfQoKcG9zdEV4Y2x1c2lvbnNDYV9SZXBsZXRlZCAlPiUKICAgbXV0YXRlKHJlcGxldGlvblJhbmdlID0gY2FzZV93aGVuKHZhbHVlbnVtIDwgMS4yICYgZmxhZyA9PSAiYWJub3JtYWwiIH4gImJlbG93IG5vcm1hbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlbnVtID4gMS4yICYgZmxhZyA9PSAiYWJub3JtYWwiIH4gImFib3ZlIHJhbmdlIixUUlVFIH4gIndpdGhpbiByYW5nZSIpKSAtPiBwb3N0RXhjbHVzaW9uc0NhX1JlcGxldGVkCgpwb3N0RXhjbHVzaW9uX2ZyZXEgPC0gcG9zdEV4Y2x1c2lvbnNDYV9SZXBsZXRlZCAlPiUKICBncm91cF9ieShyZXBsZXRpb25SYW5nZSkgJT4lCiAgc3VtbWFyaXplKG9ic2VydmF0aW9ucyA9IG4oKSkgJT4lCiAgbXV0YXRlKHBlcmNlbnRhZ2UgPSBvYnNlcnZhdGlvbnMgLyBzdW0ob2JzZXJ2YXRpb25zKSAqIDEwMCkKCmdncGxvdChwb3N0RXhjbHVzaW9uX2ZyZXEsIGFlcygiIiwgcGVyY2VudGFnZSwgZmlsbCA9IHJlcGxldGlvblJhbmdlKSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMSkgKwogIGNvb3JkX3BvbGFyKHRoZXRhID0gInkiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID1jKCAiI0IzQjNCMyIsIiMyNzQwOEIiLCIjRUU3NjIxIiksbmFtZSA9ICIgIiwgLGxhYmVscyA9IGMoIkFib3ZlIDEuMyIsICJCZWxvdyAxLjEiLCAiMS4xLTEuMyIpKSArCiAgdGhlbWVfdm9pZCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiKSAgLT4gcGllX2NoYXJ0X2NhCgpwaWVfY2hhcnRfY2EKCmBgYAoKCgojIyMgTm9uIC0gUmVwbGV0aW9uIEFuYWx5c2lzIAoKYGBge3J9CmFsbE5vblJlcGxldGlvbnNfY2EgPC0gYmluZF9yb3dzKGxpc3QocHJlX3JlcGxldGlvbiA9IG5vblJlcGxldExhYnNQcmUsIHBvc3RfcmVwbGV0aW9uID0gbm9uUmVwbGV0TGFic1Bvc3QpLCAuaWQgPSAicHJlVnNQb3N0IikKCiNSdW4gRXhjbHVzaW9ucwphbGxOb25SZXBsZXRpb25zX2NhICU+JQogIGFudGlfam9pbihoYWRtX2lkX3RhYmxlLCBieSA9ICJoYWRtX2lkIikgLT4gcG9zdGV4Y2x1c2lvbl9ub25SZXBsZXRpb25zCgpwb3N0ZXhjbHVzaW9uX25vblJlcGxldGlvbnMKYGBgCgoKIyMjIyBUYWJsZSAKCmBgYHtyfQpwb3N0ZXhjbHVzaW9uX25vblJlcGxldGlvbnMgJT4lCiAgZ3JvdXBfYnkoIHByZVZzUG9zdCkgJT4lCiAgc3VtbWFyaXplKG4gPSBuKCksIG1lYW4gPSBtZWFuKHZhbHVlbnVtLCBuYS5ybSA9IFRSVUUpLCBzdGFuZGFyZF9kZXZpYXRpb24gPSBzZCh2YWx1ZW51bSwgbmEucm0gPSBUKSkgCmBgYAoKCiMjIyBDb21iaW5pbmcgdGhlIHJlcGxldGVkIHZzIE5vblJlcGxldGVkIHByZSBsYWIgdmFsdWVzIGZvciBjb21wYXJpc29uCmBgYHtyfQpyZXBsZXRlZFZzTm9uUmVwbGV0ZWRfUHJlViA8LSBiaW5kX3Jvd3MobGlzdChyZXBsZXRlZCA9IHBvc3RFeGNsdXNpb25zQ2FfUmVwbGV0ZWQsIG5vblJlcGxldGVkID0gcG9zdGV4Y2x1c2lvbl9ub25SZXBsZXRpb25zKSwgLmlkID0gInJlcGxldGVWc05vbiIpICU+JSBmaWx0ZXIocHJlVnNQb3N0ID09ICJwcmVfcmVwbGV0aW9uIikKcmVwbGV0ZWRWc05vblJlcGxldGVkX1ByZVYgJT4lIGRpc3RpbmN0KCkKYGBgCiMjIyMgVGFibGUKYGBge3J9CiNUYWJsZQpyZXBsZXRlZFZzTm9uUmVwbGV0ZWRfUHJlViAlPiUKICBncm91cF9ieShyZXBsZXRlVnNOb24sZmxhZykgJT4lCiAgc3VtbWFyaXplKG9ic2VydmF0aW9ucyA9IG4oKSxtZWFuID0gbWVhbih2YWx1ZW51bSwgbmEucm0gPSBUKSwgc3RkX2RldiA9IHNkKHZhbHVlbnVtLCBuYS5ybT1UKSkgJT4lCiAgbXV0YXRlKHBlcmNlbnRhZ2UgPSBvYnNlcnZhdGlvbnMgLyBzdW0ob2JzZXJ2YXRpb25zKSAqIDEwMCkgCmBgYAoKCiMjIyMgVmlzdWFsaXphdGlvbgoKYGBge3J9CiNWaXN1YWxpemluZyB0aGUgbm9uLXJlcGxldGVkIHZzIHJlcGxldGVkIGxhYiB2YWx1ZXMgCmdncGFyKGdnaGlzdG9ncmFtKGRhdGEgPSByZXBsZXRlZFZzTm9uUmVwbGV0ZWRfUHJlViwgeD0idmFsdWVudW0iLCBmaWxsID0gImZsYWciLCBhZGQ9Im1lYW4iLHBvc2l0aW9uID0gImlkZW50aXR5IiwgcGFsZXR0ZSA9IGMoIiMwMEFGQkIiLCAiI0U3QjgwMCIpLGFkZF9kZW5zaXR5ID0gRkFMU0UsIGJpbnMgPSA4MCwgZ2dndGhlbWUgPSB0aGVtZV9wdWJyKCksIHhsYWIgPSAiTGFiIFZhbHVlIiwgdGl0bGUgPSAiUHJlIFZzIFBvc3QgUmVwbGV0aW9uIExhYiBWYWx1ZXMgLSBQb3Rhc3NpdW0iLCB5bGFiID0gIlJlcGxldGlvbnMiLCBmYWNldC5ieSA9ICJyZXBsZXRlVnNOb24iKQogICAgICAsIHhsaW0gPSBjKDAuNSwyKSkKYGBgCioqUXVlc3Rpb24qKjogSG93IGNhbiB0aGVyZSBiZSBoaXN0b2dyYW0gYmFycyB0aGF0IGFyZSAiYWJub3JtYWwiIG9uIHRvcCBvZiBiYXJzIHRoYXQgYXJlIHdoaXRlPyBBcmUgdGhleSBqdXN0IG5vdCBncmFudWxhciBlbm91Z2ggdG8gZGlmZmVyZW50aWF0ZSB0aGVtPyAKSSB0aGluayB0aGVyZSBhcmUgZXJyb3JzIGluIGhvdyB0aGUgZGF0YSB3YXMgZmxhZ2dlZC4gU2hvdWxkIGxvb2sgbW9yZSBpbnRvIHRoaXMuIAojIyMjIExvb2tpbmcgYXQgdGhlIG91dGxpZXJzIAoKIyMjIyMgUmVwbGV0ZWQgYWJvdmUgdGhlIHRocmVzaG9sZC4KYGBge3J9CnJlcGxldGVkVnNOb25SZXBsZXRlZF9QcmVWICU+JQogIGZpbHRlcihyZXBsZXRlVnNOb24gPT0gInJlcGxldGVkIikgJT4lCiAgZmlsdGVyKGZsYWcgPT0gImFibm9ybWFsIiAmIHZhbHVlbnVtID4gNCkgJT4lCiAgdW5ncm91cCgpJT4lCiAgc3VtbWFyaXplKG9ic2VydmF0aW9ucyA9IG4oKSxtZWFuID0gbWVhbih2YWx1ZW51bSwgbmEucm0gPSBUKSwgc3RkX2RldiA9IHNkKHZhbHVlbnVtLCBuYS5ybT1UKSkgCmBgYAoKIyMjIyMgRGlkbid0IHJlcGxldGUgQmVsb3cgdGhlIHRocmVzaG9sZC4KCiMjIyMjIyBUYWJsZSAKYGBge3J9CnBvc3RleGNsdXNpb25fbm9uUmVwbGV0aW9ucyAlPiUKICBmaWx0ZXIoZmxhZyA9PSAiYWJub3JtYWwiICYgdmFsdWVudW0gPCA0KSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc3VtbWFyaXplKG9ic2VydmF0aW9ucyA9IG4oKSxtZWFuID0gbWVhbih2YWx1ZW51bSwgbmEucm0gPSBUKSwgc3RkX2RldiA9IHNkKHZhbHVlbnVtLCBuYS5ybT1UKSkgCmBgYAoKIyMjIyMjIFZpc3VhbGl6YXRpb24gb2Ygbm90IHJlcGxldGlvbmcgYmVsb3cgdGhlIHRocmVzaG9sZAoKYGBge3J9CnBvc3RleGNsdXNpb25fbm9uUmVwbGV0aW9ucyAlPiUKICBmaWx0ZXIodmFsdWVudW0gPCAzLjUgJiBmbGFnID09ICJhYm5vcm1hbCIpICU+JQogIGdncGxvdChhZXMoeCA9IHZhbHVlbnVtKSkgKwogIGdlb21faGlzdG9ncmFtKGFscGhhPTAuNiwgcG9zaXRpb249ImlkZW50aXR5IixiaW5zPTE0LCBjb2xvcj0iYmxhY2siKSArIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDEsMy41KSkgKyB0aGVtZV9wdWJyKCkKYGBgCgoKIyMjQW1vdW50IG9mIHBlb3BsZSB0aGF0IHJlcGxldGUgKHZzIE5vbi1SZXBsZXRlKSBhY3Jvc3MgbGFiIHZhbHVlIAoKYGBge3J9CnJlcGxldGVkVnNOb25SZXBsZXRlZF9QcmVWICU+JQogIGdyb3VwX2J5KHZhbHVlbnVtLCByZXBsZXRlVnNOb24pICU+JQogIHN1bW1hcml6ZShvYnNlcnZhdGlvbnMgPSBuKCkgKSAlPiUKICBnZ3Bsb3QoYWVzKGZpbGw9cmVwbGV0ZVZzTm9uLCB5PW9ic2VydmF0aW9ucywgeD12YWx1ZW51bSkpICsgCiAgICBnZW9tX2Jhcihwb3NpdGlvbj0iZmlsbCIsIHN0YXQ9ImlkZW50aXR5IikgKyBzY2FsZV94X2NvbnRpbnVvdXMoKSAgKyB0aGVtZV9wdWJyKCkKYGBgCgojIyBUaW1lIG9mIERheSBBbmFseXNpcyAKRXhhbWluaW5nIHRoZSB0aW1lIG9mIGRheSBmb3IgZWFjaCByZXBsZXRpb24gYW5kIGxhYiB2YWx1ZS4gCgpgYGB7cn0KcmVwbGV0ZWRWc05vblJlcGxldGVkX1ByZVYgJT4lCiAgbXV0YXRlKFJlcGxldGlvbkhvdXIgPSBob3VyKHN0YXJ0dGltZSksIExhYkhvdXIgPSBob3VyKGNoYXJ0dGltZS5sYWIpKSAlPiUKICBnZ2hpc3RvZ3JhbSh4ID0gYygiTGFiSG91ciIsIlJlcGxldGlvbkhvdXIiKSwgYmlucyA9IDI0LCBwYWxldHRlICA9ICJ1Y2hpY2FnbyIsIGFscGhhID0gMC41LCBtZXJnZSA9IFQsIHhsYWIgPSAiSG91cnMiLCB5bGFiID0gIk51bWJlciBvZiBPY2N1cmVuY2VzIikKCnJlcGxldGVkVnNOb25SZXBsZXRlZF9QcmVWICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoTGFiSG91ciA9IGhvdXIoY2hhcnR0aW1lLmxhYikgKyA0LCBSZXBsZXRpb25Ib3VyID0gaG91cihzdGFydHRpbWUpKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IGMoTGFiSG91cixSZXBsZXRpb25Ib3VyKSwgdmFsdWVzX3RvPSJIb3VyIiwgbmFtZXNfdG89IldoaWNoSG91ciIpIC0+IHl5CgpvcmRlcmVkKHl5JFdoaWNoSG91cikKeXkkV2hpY2hIb3VyIDwtIGZhY3Rvcih5eSRXaGljaEhvdXIsIG9yZGVyZWQgPSBGQUxTRSkKeXkkV2hpY2hIb3VyIDwtIHJlbGV2ZWwoeXkkV2hpY2hIb3VyLCJSZXBsZXRpb25Ib3VyIikKZ2dwbG90KHl5LCBhZXMoeCA9IEhvdXIsZmlsbD1XaGljaEhvdXIpKSArIGdlb21faGlzdG9ncmFtKHBvc2l0aW9uPSJpZGVudGl0eSIsYmlucyA9IDI0LGFscGhhPTAuNikgKyAKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDI0LDIpKSAgKyB0aGVtZV9wdWJyKCkKCmdncGxvdCh5eSwgYWVzKHggPSBIb3VyLGZpbGw9V2hpY2hIb3VyKSkgKwogIGdlb21faGlzdG9ncmFtKGFscGhhPTAuNCwgcG9zaXRpb249ImlkZW50aXR5IixiaW5zPTI0LCBjb2xvcj0iYmxhY2siKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsMjQsMikpICsgZmFjZXRfd3JhcCh2YXJzKHJlcGxldGVWc05vbikpCmBgYAoKIyMjIyBQcmUtUmVwbGV0aW9uIHZzIE5vbi1SZXBsZXRlZCBMYWIgVmFsdWVzIApCZWxvdywgd2UgaGF2ZSB0aGUgdGltZSBvZiBkYXkgZm9yIGxhYiB2YWx1ZSBkcmF3cyB0aGF0IG9jY3VyIGZvciBsYWIgdmFsdWVzIHRoYXQgd2VyZSByZXBsZXRlZCwgYW5kIHRob3NlIHRoYXQgZGlkIG5vdCBkaXJlY3RseSBsZWFkIHRvIGEgcmVwbGV0aW9uLiAKCkl0IHNlZW1zIHRoYXQgdGhvc2UgbGVhZGluZyB0byBhIHJlcGx0aW9uIHdlcmUgbW9yZSBsaWtlbHkgdG8gb2NjdXIgaW4gdGhlIG1vcm5pbmcsIHdoaWxlIHRob3NlIHRoYXQgZGlkIG5vdCBkaXJlY3RseSBsZWFkIHRvIGEgcmVwbGV0aW9uIHdlcmUgbW9yZSBsaWtlbHkgdG8gb2NjdXIgaW4gdGhlIGFmdGVybm9vbi4gCgpXaHkgaXMgdGhpcyBvY2N1cnJpbmc/IApQZXJoYXBzLCByZXBsZXRpb25zIGluIHRoZSBtb3JuaW5nIGFyZSAgcm91dGluZSwgYnV0IHRoZXJlIGlzIG5vIHJvdXRpbmUgaW4gcGVyZm9ybWluZyByZXBsZXRpb25zIGluIHRoZSBhZnRlcm5vb24uIApgYGB7cn0KcmVwbGV0ZWRWc05vblJlcGxldGVkX1ByZVYgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShMYWJIb3VyID0gaG91cihjaGFydHRpbWUubGFiKSkgJT4lCiAgZmlsdGVyKHByZVZzUG9zdCA9PSAicHJlX3JlcGxldGlvbiIpICU+JQogIGdncGxvdChhZXMoeCA9IExhYkhvdXIsZmlsbD1yZXBsZXRlVnNOb24pKSArIGdlb21faGlzdG9ncmFtKHBvc2l0aW9uPSJpZGVudGl0eSIsYmlucyA9IDI0LGFscGhhPTAuNiwgY29sb3IgPSAiYmxhY2siKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsMjQsMikpICArIHRoZW1lX3B1YnIoKSArIHhsYWIoIkhvdXIgb2YgRGF5IikgKyB5bGFiKCJOdW1iZXIgb2YgTGFiIFZhbHVlcyIpICsgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lID0gIiIsIGxhYmVscyA9IGMoIk5vbi1SZXBsZXRpb24iLCAiUmVwbGV0aW9uIikpCgoKYGBgCgojIyMjIEF2ZXJhZ2UgcHJlLXJlcGxldGlvbiBsYWIgdmFsdWUgYXQgZWFjaCBob3VyIAojIyMjIyBUYWJsZQpgYGB7cn0KcmVwbGV0ZWRWc05vblJlcGxldGVkX1ByZVYgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShMYWJIb3VyID0gaG91cihjaGFydHRpbWUubGFiKSkgJT4lCiAgZmlsdGVyKHByZVZzUG9zdCA9PSAicHJlX3JlcGxldGlvbiIpICU+JQogIGdyb3VwX2J5KExhYkhvdXIsIHJlcGxldGVWc05vbikgJT4lCiAgc3VtbWFyaXplKG1lYW5fbGFiX3ZhbHVlID0gbWVhbih2YWx1ZW51bSwgbmEucm0gPSBUKSkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHJlcGxldGVWc05vbiwgdmFsdWVzX2Zyb20gPSBtZWFuX2xhYl92YWx1ZSkKYGBgCgoKIyMjIyMgVmlzdWFsaXphdGlvbgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcmVwbGV0ZWRWc05vblJlcGxldGVkX1ByZVYgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShMYWJIb3VyID0gaG91cihjaGFydHRpbWUubGFiKSkgJT4lCiAgZmlsdGVyKHByZVZzUG9zdCA9PSAicHJlX3JlcGxldGlvbiIpICU+JQogIGdyb3VwX2J5KExhYkhvdXIsIHJlcGxldGVWc05vbikgJT4lCiAgc3VtbWFyaXplKG1lYW5fbGFiX3ZhbHVlID0gbWVhbih2YWx1ZW51bSwgbmEucm0gPSBUKSkgJT4lCiAgZ2diYXJwbG90KHg9IkxhYkhvdXIiLCB5ID0gIm1lYW5fbGFiX3ZhbHVlIiwgZmlsbCA9ICJyZXBsZXRlVnNOb24iLCBmYWNldC5ieSA9ICJyZXBsZXRlVnNOb24iICkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsMjQsMikpICArIHRoZW1lX3B1YnIoKQoKcmVwbGV0ZWRWc05vblJlcGxldGVkX1ByZVYgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShMYWJIb3VyID0gaG91cihjaGFydHRpbWUubGFiKSkgJT4lCiAgZmlsdGVyKHByZVZzUG9zdCA9PSAicHJlX3JlcGxldGlvbiIpICU+JQogIGdyb3VwX2J5KExhYkhvdXIsIHJlcGxldGVWc05vbikgJT4lCiAgc3VtbWFyaXplKG1lYW5fbGFiX3ZhbHVlID0gbWVhbih2YWx1ZW51bSwgbmEucm0gPSBUKSkgJT4lCiAgZ2dwbG90KGFlcyh4PUxhYkhvdXIseT1tZWFuX2xhYl92YWx1ZSxncm91cD1yZXBsZXRlVnNOb24sIGNvbG9yID0gcmVwbGV0ZVZzTm9uKSkgKyBnZW9tX2xpbmUoKSArIGdlb21fcG9pbnQoKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsMjQsMikpICArIHRoZW1lX3B1YnIoKQoKCnJlcGxldGVkVnNOb25SZXBsZXRlZF9QcmVWICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoTGFiSG91ciA9IGhvdXIoY2hhcnR0aW1lLmxhYikpICU+JQogIGZpbHRlcihwcmVWc1Bvc3QgPT0gInByZV9yZXBsZXRpb24iKSAlPiUKICBncm91cF9ieShMYWJIb3VyLCByZXBsZXRlVnNOb24pICU+JQogIHN1bW1hcml6ZShtZWFuX2xhYl92YWx1ZSA9IG1lYW4odmFsdWVudW0sIG5hLnJtID0gVCkpICU+JQogIGdncGxvdChhZXMoeD1MYWJIb3VyLHk9bWVhbl9sYWJfdmFsdWUsZmlsbD1yZXBsZXRlVnNOb24pKSArIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiLCBjb2xvciA9ICJibGFjayIpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwyNCwyKSkgICsgdGhlbWVfcHVicigpCmBgYAoKCg==